
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Text;
using System.Xml;
using Swaf;
using Swaf.BizObj.fieldValidator;
using Swaf.Container;

namespace Swaf.BizObj
{
	[Serializable]
	public class BizObjDefinition : IBizObjDefinition, IBizObjDefinitionEx2, ICatalogItem
	{
		protected string m_name;
		protected IBizObjDefinition m_parent = null;

		protected Hashtable m_fields = new Hashtable(new CaseInsensitiveHashCodeProvider(), new CaseInsensitiveComparer());
		protected ListDictionary m_keyFields;
		protected ArrayList m_origionalOrderFields = new ArrayList();

		protected bool m_injectionFieldsOnly = false;
        
        #region ICatalogItem members

        public static readonly string DefaultExpiration = string.Empty;
        
        private bool m_isCacheable = true;
        private string m_searchPath = string.Empty;
        private string m_expiration = string.Empty;
        private string m_key = string.Empty;
        private string m_catalogName = string.Empty;
        
        #endregion

        public BizObjDefinition(string name, string keyFieldNames)
		{
			m_name = name;
			setKeyFields(keyFieldNames);
		}

		public BizObjDefinition(string name, bool injectionFieldsOnly)
		{
			m_name = name;
			m_injectionFieldsOnly = injectionFieldsOnly;
			setKeyFields(null);
		}
        
        public static IBizObjDefinition createInstance(XmlElement req, string catalogName)
        {
            // Create a BizObjDef from the xml
            BizObjDef bizobjdef = new BizObjDef(req);
            
            // Create an instance of IBizObjDefition using the BizObjDef method
            BizObjDefinition def = bizobjdef.create(false) as BizObjDefinition;

            Debug.Assert(def != null, "A different implementation that BizObjDefinition was use to create the IBizObjDefition instance");

            // Set the expiration
            if (def.expiration == string.Empty)
            {
                // Get the default expiration if it is specified
                string boExpiration = Application.currentApp.resMgr.resolve("$DynamicBizObjExpiration$");
                if (boExpiration == null || boExpiration == string.Empty || boExpiration[0] == '$')
                {
                    boExpiration = BizObjDefinition.DefaultExpiration;
                }

                def.expiration = boExpiration;
            }

            return def;
        }

		public bool isKindOf(string bizobjType)
		{
			BizObjDefinition def = this;
			while (def != null)
				if (def.Name == bizobjType)
					return true;
				else
					def = def.Parent as BizObjDefinition;
			return false;
		}

		protected internal void setKeyFields(string keyFieldNames)
		{
			if (keyFieldNames != null)
			{
				string[] sa = keyFieldNames.Split(',');
				m_keyFields = new ListDictionary();
				//Until the actual fields are defined for this bizobj we can only
				//store the names of the fields.  As fields are added to the bizobj definition
				//this sorted list will be updated from null to IBizObjField.
				foreach (string key in sa)
					m_keyFields[key.Trim()] = null;
			}
			else
				m_keyFields = new ListDictionary();
		}

		protected internal bool IsInjectedFieldsOnly
		{
			get { return m_injectionFieldsOnly; }
			set { m_injectionFieldsOnly = value; }
		}

		#region IBizObjDefinition Members

		public string Name
		{
			get { return m_name; }
		}

		public IBizObjDefinition Parent
		{
			get { return m_parent; }
		}


		internal void setParent(IBizObjDefinition parent)
		{
			m_parent = parent;
			ArrayList orderedFields = new ArrayList(parent.OrderedFields.Count);
			foreach (BizObjField field in parent.OrderedFields)
			{
				if (m_fields.Contains(field.Name))
				{
					//This would only happen if this bizobj had its parent set to a bizobj definition that was
					//really just an injected field.  Those fields should be blown off.
					BizObjField containedField = m_fields[field.Name] as BizObjField;
					if (containedField.InheritedField == false)
						throw new BadBizObjFieldException(m_name, field.Name, "Trying to redefine a field that was defined in the parent");
				}
				else
				{
					BizObjField clonedField = field.Clone() as BizObjField;
					clonedField.InheritedField = true;
					m_fields[clonedField.Name] = clonedField;
					//m_fields[clonedField.Name.ToLower()] = clonedField;
					if (m_keyFields.Contains(clonedField.Name))
						m_keyFields[clonedField.Name] = clonedField;
					orderedFields.Add(clonedField);
				}
			}
			//We rebuild the ordered fields because the parent's fields should always be first in the list.
			//any fields already in the ordered field list need to be placed after the parents.  This
			//can happen when the parent bizobj is defined after the child in configuration.
			foreach (BizObjField field in m_origionalOrderFields)
				orderedFields.Add(field);
			m_origionalOrderFields = orderedFields;
		}

		public IList KeyFields
		{
			get { return new ArrayList(m_keyFields.Values); }
		}

		public string KeyFieldNames
		{
			get
			{
				StringBuilder buff = new StringBuilder();
				foreach (string keyName in m_keyFields.Keys)
					buff.Append(keyName).Append(",");
				return buff.Length == 0?"":buff.ToString(0, buff.Length - 1); //-1 in order to strip the trailing "," added at the end.
			}
		}

		public IDictionary Fields
		{
			get { return m_fields; }
		}

		public IList FieldList
		{
			get { return m_origionalOrderFields; }
		}

		public IList OrderedFields
		{
			get { return m_origionalOrderFields; }
		}

		public IBizObjField getField(string name, bool caseSensitive)
		{
            if (NameAddress.isSingleAddress(name))
                return m_fields[name] as IBizObjField;
            else
            {
                IBizObjField fld = m_fields[NameAddress.getFirstAddress(name)] as IBizObjField;
				if (fld == null || fld.getBizObjType() == null)
					return null;
                IBizObjDefinition def = Application.currentApp.bizObjMgr.getDefinition(fld.getBizObjType());
                return def.getField(NameAddress.getRemainingAddress(name), false);
            }
		}

		public IComparable convertValueToKeyType(string val)
		{
			Debug.Assert(val != null);
			IList fields = KeyFields;
			if (fields.Count == 0)
				return null;
			if (fields.Count > 1)
				return val;
			if (((BizObjField)fields[0]).BizType == BizFieldType.Method)
				return val;
			return (IComparable)Application.currentApp.factories["BizObjBizFieldTypes"].createFromNickName(((BizObjField)fields[0]).FactoryType, val);
		}

		#endregion

		#region IBizObjDefinitionEx Members

		public IBizObjField addField(string name, BizFieldType BizFieldType, object newValue, string actualFieldType)
		{
			int index = m_origionalOrderFields.Count;
			BizObjField field = new BizObjField(this, index, name, BizFieldType, newValue, actualFieldType, false);
			addField(field);
			return field;
		}

		public IBizObjField decorateField(string name)
		{
			//Not implemented in origional bizobj either.
			throw new BaseException("The method or operation is not implemented.");
		}

		#endregion

		internal void addField(BizObjField field)
		{
			int origionalFieldPos = -1;
			for(int pos = 0; pos < m_origionalOrderFields.Count; pos++)
				if (string.Compare(field.Name, ((BizObjField)m_origionalOrderFields[pos]).Name, false) == 0)
				{
					origionalFieldPos = pos;
					break;
				}

			field.setBizObjIndex(origionalFieldPos != -1 ? origionalFieldPos : m_origionalOrderFields.Count);
			m_fields[field.Name] = field;
			//m_fields[field.Name.ToLower()] = field;
			if (origionalFieldPos != -1)
				m_origionalOrderFields[origionalFieldPos] = field;
			else
				m_origionalOrderFields.Add(field);
			if (m_keyFields.Contains(field.Name))
				m_keyFields[field.Name] = field;
		}

		public void removeField(string name)
		{
			IBizObjField field = m_fields[name] as IBizObjField;
			if(field != null)
			{
				m_fields.Remove(field);
				if (m_keyFields.Contains(field))
					m_keyFields.Remove(field);
				if (m_origionalOrderFields.Contains(field))
					m_origionalOrderFields.Remove(field);

			}
		}

        public string catalogName
        {
            get { return m_catalogName; }
            set { m_catalogName = value; }
        }

        public string key
        {
            get { return m_key; }
            set { m_key = value; }
        }

        public string expiration
        {
            get { return m_expiration; }
            set { m_expiration = value; }
        }

        public string searchPath
        {
            get { return m_searchPath; }
            set { m_searchPath = value; }
        }

        public bool isCacheable
        {
            get { return m_isCacheable; }
        }
	}

}
